/**
 * 
 */
package cn.edu.ustc.multicore.exercise.ch4;

import java.util.concurrent.locks.Lock;

import cn.edu.ustc.multicore.exercise.ch1.PrimeSearching;

/**
 * @author dragon
 * 
 */
public class LockTest extends Thread {

	private Lock mutex = null;
	private int accessNum;

	private long counter = 0;

	/**
	 * 
	 * @param lock
	 * @param accessNum
	 */
	public LockTest(Lock lock, int accessNum) {
		this.mutex = lock;
		this.accessNum = accessNum;
	}

	public void run() {
		for (int i = 0; i < this.accessNum; i++) {
			mutex.lock();
			try {
				// access mutex section
				CriticalSection.getInstance().access();
			} finally {
				mutex.unlock();
			}
		}
	}

	public static void printResult(String program, int threads, long count,
			double usedTime) {
		System.out.println("the result of lock performance as below: ");
		System.out.println("---------------------------------------");
		System.out.println("-> used lock: " + program);
		System.out.println("-> used threads: " + threads);
		System.out.println("-> access critical section times: " + count);
		System.out.println("-> used time(s): " + usedTime);
		System.out.println("---------------------------------------\n\n");
	}

	/**
	 * 
	 * @param lock
	 * @param threads
	 *            how many threads to access critical section
	 * @param enterTimes
	 *            how many times to access critical section for one thread
	 */
	public static void test(Lock lock, int threads, int enterTimes) {
		// reset Critical Section data
		CriticalSection.getInstance().reset();
		long startTime = 0l;
		long endTime = 0l;
		double usedSecond = 0.000;
		try {
			startTime = System.currentTimeMillis();
			LockTest[] users = new LockTest[threads];
			for (int i = 0; i < threads; i++) {
				users[i] = new LockTest(lock, enterTimes);
				users[i].start();
			}
			for (int i = 0; i < threads; i++) {
				users[i].join();
			}
			endTime = System.currentTimeMillis();
			usedSecond = (double) (endTime - startTime) / 1000;
			printResult(lock.getClass().getName(), threads, CriticalSection
					.getInstance().getAccessTimes(), usedSecond);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		// how many threads to access critical section
		int threads = 4;

		// parse the parameter, and assign them
		if (args != null & args.length > 0) {
			boolean correct = true;
			for (int i = 0; i < args.length; i++) {
				if ("-threads".equals(args[i])) {
					if (args[i + 1] != null) {
						threads = Integer.valueOf(args[i + 1]);
						i++;
						continue;
					} else {
						correct = false;
						break;
					}
				}

			}

			if (!correct) {
				System.out.println("Usage: " + PrimeSearching.class.getName()
						+ " -length number -unit number ");
				return;
			}
		}
		// how many times to access critical section for one thread
		int enterTimes = 1000000;

		// #1 test for TASLock
		TASLock taslock = new TASLock();
		test(taslock, threads, enterTimes);

		// #2 test for TTASLock
		TTASLock ttaslock = new TTASLock();
		test(ttaslock, threads, enterTimes);

		// #3 test for TTASLock
		BackoffLock backofflock = new BackoffLock();
		test(backofflock, threads, enterTimes);

		// #4 test for ALock
		// ALock alock = new ALock(threads);
		// test(alock,threads,enterTimes);

		// #5 test for CLHLock
		// CLHLock clhlock = new CLHLock();
		// test(clhlock,threads,enterTimes);

		enterTimes = 10000;
		threads = 4;
		MCSLock mcslock = new MCSLock();
		test(mcslock, threads, enterTimes);

	}

}
